package org.ecpkn.PAGridSim.core.offlineOptimization;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.ecpkn.PAGridSim.core.configuration.Configuration;
import org.ecpkn.PAGridSim.core.tasksCreation.Task;

public abstract class ChangingWorkloadOptimizationAlgo {

	private static final Logger logger = LogManager
			.getLogger(ChangingWorkloadOptimizationAlgo.class);

	protected static ArrayList<Task> tasks_;
	public static HashMap<Double, ArrayList<Task>> changingTasks_;
	public static HashMap<Double, Double> workload_;

	public static SortedSet<Double> sortedTimePoints;
	protected static HashMap<Double, OptimizedResultEntry> result_;
	// inutile d'avoir time format avec decimal trop long
	private static DecimalFormat dfInt = new DecimalFormat("#");

	public ChangingWorkloadOptimizationAlgo(ArrayList<Task> tasks) {
		tasks_ = tasks;
		workload_ = new HashMap<Double, Double>();
		changingTasks_ = new HashMap<Double, ArrayList<Task>>();
		try {
			workloadTrace(tasks_);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * @param tasks
	 * @throws Exception
	 */
	public void workloadTrace(ArrayList<Task> tasks) throws Exception {
		// get time point important
		ArrayList<Double> timePoints = new ArrayList<Double>();
		for (int i = 0; i < tasks_.size(); i++) {
			Task t = tasks_.get(i);
			double time = t.getSubmitedTime_();
			if (!timePoints.contains(time)) {
				timePoints.add(time);
			}
			time = t.getEndTime();
			if (!timePoints.contains(time)) {
				timePoints.add(time);
			}
		}
		// TreeSet makes timePoints in order
		sortedTimePoints = new TreeSet<Double>(timePoints);

		for (Double timePoint : sortedTimePoints) {
			for (Task t : tasks_) {
				if (t.getSubmitedTime_() <= timePoint
						&& t.getEndTime() > timePoint) {
					if (changingTasks_.get(timePoint) != null) {
						changingTasks_.get(timePoint).add(t);
					} else {
						ArrayList<Task> taskList = new ArrayList<Task>();
						taskList.add(t);
						changingTasks_.put(timePoint, taskList);
					}
				}
			}
			if (changingTasks_.get(timePoint) == null) {
				changingTasks_.put(timePoint, new ArrayList<Task>());
			}
			Double totalLoad = 0.0;
			for (Task t : changingTasks_.get(timePoint)) {
				double singleLoad = singleTaskLoad(t);
				totalLoad = singleLoad + totalLoad;
			}
			workload_.put(timePoint, Double.valueOf(Configuration.getInstance()
					.toDecimalFormatDouble(totalLoad)));
		}
	}

	public void printChangingWorkloadOptimizationTrace() throws Exception {
		for (Double key : sortedTimePoints) {
			StringBuilder taskInfo = new StringBuilder(100);
			// TimePoint
			taskInfo.append("TimePoints:\t").append(toDecimalFormat(key));
			// Workload
			taskInfo.append("\tWorkload: \t").append(
					toDecimalFormat(workload_.get(key)));
			// OptimizedResult
			// *Frequency
			taskInfo.append("\tOptimizedResult: optF=\t").append(
					toDecimalFormat(result_.get(key).getOptimizedFrequency_()));
			// *Node Number
			taskInfo.append("\toptN=\t").append(
					dfInt.format(result_.get(key).getOptimizedNodesNumber_()));
			// *Utilization
			taskInfo.append("\toptU=\t")
					.append(toDecimalFormat(result_.get(key)
							.getOptimizedUtilization_()));
			// *Theoretical Power
			taskInfo.append("\toptP=\t").append(
					toDecimalFormat(result_.get(key)
							.getTheoreticalOptimizedPower_()));
			// *Tasks Id
			taskInfo.append("\tTasks(Id):\t");
			for (int index = 0; index < changingTasks_.get(key).size(); index++) {
				taskInfo.append(changingTasks_.get(key).get(index).getTaskID_()
						+ ",");
			}
			logger.trace(taskInfo.toString());
		}
	}

	private String toDecimalFormat(Double key) {
		return Configuration.getInstance().toDecimalFormat(key);
	}

	public HashMap<Double, OptimizedResultEntry> optimize() throws Exception {
		result_ = new HashMap<Double, OptimizedResultEntry>();
		for (Double key : sortedTimePoints) {
			OptimizedResultEntry optRE = singleWorkloadOptimization(workload_
					.get(key));
			optRE.setTimePoint(key);
			result_.put(key, optRE);
		}
		printChangingWorkloadOptimizationTrace();
		return result_;
	}

	/**
	 * should be override
	 * 
	 * @param task
	 *            t
	 * @return singleLoad
	 */
	protected double singleTaskLoad(Task t) throws Exception {
		throw new Exception(
				"ChangingWorkloadOptimization: singleLoad Method not yet implemented");
	}

	/**
	 * should be override
	 * 
	 * @param workload
	 * @return
	 * @throws Exception
	 */
	protected OptimizedResultEntry singleWorkloadOptimization(double workload)
			throws Exception {
		throw new Exception(
				"ChangingWorkloadOptimization: singleWorkloadOptimization Method not yet implemented");

	}

}
